package com.hb.G_协程

import kotlinx.coroutines.*
import kotlin.concurrent.thread
import kotlin.coroutines.CoroutineContext


/**
 * 是什么？
 * 更轻量级的线程，运行在线程中
 *
 * 为什么？
 * 更轻量级的线程，线程切换开销将会变得不可忽视。在kotlin中引入了协程。
 * 为什么说协程是更轻量级的线程呢？ ==》
 * ===》 当操作系统切换线程的时候，会产生一定的消耗。而协程不一样，协程是线程的一部分，
 * 或者谁协程是包含于线程的，也就是说协程是工作在线程之上的，协程的切换可以有程序来控制
 * 不需要操作系统去进行调度，这样的话就大大降低了开销。
 *
 * 怎么做？
 * 看下面案例 ：几种创建协程的方式
 *
 * 1. Kotlin 是一门仅在标准库中提供最基本底层 API 以便各种其他库能够利用协程的语言。
 * 与许多其他具有类似功能的语言不同，async 与 await 在 Kotlin 中并不是关键字，
 * 甚至都不是标准库的一部分。此外，Kotlin 的 挂起函数 概念为异步操作提供了比 future 与 promise 更安全、更不易出错的抽象。
 *
 * 2. kotlinx.coroutines 是由 JetBrains 开发的功能丰富的协程库。它包含本指南中涵盖的很多启用高级协程的原语，包括 launch、 async 等等。
 *
 * 3. 协程的描述：
 *      协程就像非常轻量级的线程。线程是由系统调度的，线程切换或线程阻塞的开销都比较大。而协程依赖于线程，但是协程挂起时不需要
 *      阻塞线程，几乎是无代价的。协程是由开发者控制的。所以协程意向用户态的线程，非常轻量级，一个线程中可以创建任意个协程
 *
 * 4. 总而言之：
 *      协程可以简化异步编程，可以顺序的表达程序，协程也提供了一种避免阻塞线程并用更廉价、更可控的操作替代线程阻塞的方法--协程挂起
 *
 *
 * 5. 协程的重要概念：
 *       1. CoroutineScope,可以理解为协程本身，包含了CoroutineContext.
 *
 *       2. CoroutineContext,协程上下文，是一些元素的集合，主要包括Job和CoroutineDispatcher
 *       元素，可以代表一个协程的场景。
 *
 *       3. EmptyCoroutineContext表示一个空的协程上下文
 *
 *       4. CoroutineDispatcher，协程调度器，决定协程所在的线程或线程池。它可以指定协程运行于特定的一个线程、
 *       一个线程池或者不指定任何线程（这样协程就会运行于当前线程）
 *
 *       5. coroutines-core中CoroutineDispatcher有三种标准的实现：Dispatchers.Default、Dispatchers.IO,Dispatchers.Main
 *       和Dispatchers.Unconfined,Unconfined就是不指定线程
 *
 *       6. launch函数定义如果不指定CoroutineDispatcher或者没有其他的ContinuationInterceptor,默认的协程调度器就是Dispatchers.Default，
 *       Default是一个协程调度器，其指定的线程为共有的线程池，线程数量至少为2，最大与cpu数相同
 *
 *       7. Job & Deferred，是父子关系, Deferred继承了Job。Job，任务，封装了协程中需要执行的额代码逻辑。Job可以取消并且有简单的声明周期。
 *       他有三种状态：
 *
 *       8. Job完成时时没有返回值的，如果需要返回值的话，应该使用Deferred,它是Job的子类
 *       public interface Deferred<out T>:Job
 *
 *       9. CoroutineScope.launch函数属于协程构建器 Coroutine builders,Kotlin中还有其他几种Builders,负责创建协程。
 *
 *       10. CoroutineScope.launch{}是最常用的Coroutine builders，不阻塞当前线程，在后台创建一个新协程，也可以指定协程调度器
 *
 *       11. runBlocking{}是创建一个新的协程同时阻塞当前线程，直到协程结束。这个不应该在协程中使用，主要是为main函数和测试设计的
 *
 *       12. withContext{}不会创建新的协程，在指定协程上运行挂起的代码块，并挂起该协程直至代码块运行完成
 *
 *       13. CoroutineScope.async{}可以实现与launch builder一样的效果，在后台创建一个新协程，唯一的区别是他有返回值，因为
 *        CoroutineScope.async{}返回的是Deferred类型。
 *
 */
fun main() {
    /**
     * GlobalScope 继承了 CoroutineScope,
     * GlobalScope.launch相当于创建了一个协程，里面有delay方法，但是他是不会阻塞
     * 线程的执行，这里是有一个main线程的，所以他不会去阻塞线程的执行.
     *
     * GlobalScope.launch启动的协程有点像守护线程。当虚拟机退出的时候，守护线程也会消亡掉
     */
    GlobalScope.launch {
        /**
         * launch启动一个协程
         *
         * delay()的作用:他会阻塞当前协程的执行，不会阻塞当前线程的执行，只可以在协程中执行，不能再协程外执行
         *
         * 将协程延迟给定时间而不阻塞线程，并在指定时间后恢复它。
         * 该挂起功能是可以取消的。 如果在等待此挂起功能的过程中当前协程的Job被取消或完成，
         * 则此函数将立即使用CancellationException恢复。注意，可以在onTimeout子句的select调用中使用delay。
         * 实施说明：如何精确跟踪时间是CoroutineDispatcher在上下文中的实施细节。
         */
        delay(1000)// 非阻塞的等待 1 秒钟（默认时间单位是毫秒）
        println("kotlin coroutines")// 在延迟后打印输出
    }
    println("hello")//协程已在等待时主线程还在继续

    Thread.sleep(2000)//阻塞主线程 2 秒钟来保证 JVM 存活
    //Thread.sleep(500)
    /**
     * 如果当前的线程睡眠的是小于协程的暂停时间，线程都执行完了，协程里面的东西就不会去执行
     * 因为协程是依赖于线程的。如果这里睡眠的时间是500ms,
     * 输出：
     * hello
     * world
     */
    println("world")

    /**
     * 输出
     * hello
     * kotlin coroutines
     * world
     */


    //协程是更轻量级的线程，下面我们启动了10万个协程去执行一个输出Hello的操作，当我们
    //执行这段代码之后，会陆续打印出Hello,但是如果我们启动10万个线程去做的话，就可能会
    //出现"out of memory"的错误
    runBlocking {
        repeat(100_000) {
            launch {
                println("Hello"+it)
            }
        }
    }
}

/**
 *
 * 1. 协作程序，解决异步问题
 * 2. 应用层完成的调度
 *
 * 要解决的问题：
 *  1. 异步代码像同步代码一样直观
 *  2. 简化异步代码的处理
 *  3. 轻量级的并发方案
 *
 *  如何支持协程
 *      1. 编译器对suspend函数的编译支持
 *      2. 标准库的基本API支持
 *      3. kotlin.coroutine应用级的支持
 * */